package com.cictec.middleware.core.service;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.cictec.middleware.commons.cache.ecache.EHCacheUtils;
import com.cictec.middleware.commons.utils.PMSUtils;
import com.cictec.middleware.commons.utils.gps.CoordinateConversion;
import com.cictec.middleware.commons.utils.gps.CoordinateConversion.UTMPoint;
import com.cictec.middleware.commons.utils.gps.GpsUtils;
import com.cictec.middleware.core.entity.BusLineMapPoint;
import com.cictec.middleware.core.exception.BusLineMapPointException;
import com.cictec.middleware.core.mapper.BusLineMapPointMapper;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.operation.buffer.BufferOp;
import com.vividsolutions.jts.operation.buffer.BufferParameters;

/**
 * 线路信息-线路地图service
 * @author ql
 *
 */
@Service
public class BusLineMapPointService {

	public static Logger logger = LoggerFactory.getLogger(BusLineMapPointService.class);

	@Autowired
	private BusLineMapPointMapper busLineMapPointMapper;
	
	private static String LINE_MAP_LINE = "bus-line-map-segment"; 				  	// 线路的地图线段
	private double maxOffLine = 100;
	
	
	/**
	 * 刷新线路地图缓存
	 * @param lineUuid
	 * @param lineType
	 */
	public void reloadBusLineMapPoint(String lineUuid, String lineType){
		if(PMSUtils.isEmpty(lineType)){
			try {
				throw new Exception("未指定线路类型");
			} catch (Exception e) {
				e.printStackTrace();
				return;
			}
		}

		String cacheKey = lineUuid + "-" + lineType;

		List<BusLineMapPoint> lineMapPoints = selectListBusLineMapPoint(lineUuid, lineType);
		for (BusLineMapPoint ml : lineMapPoints) {
			ml.setUtmPoint(CoordinateConversion.latLon2UTM(Double.valueOf(ml.getMlLat()), Double.valueOf(ml.getMlLng())));
		}

		EHCacheUtils.setValue(LINE_MAP_LINE, cacheKey, lineMapPoints);
	}
	
	
	/**
	 * 【缓存】根据站点序号，获取地图线段站点坐标
	 * @param lineUuid
	 * @param lineType
	 * @param stationSequence 线路站点序号
	 * @return
	 */
	public BusLineMapPoint getBusLineMapPoint(String lineUuid, String lineType, int stationSequence){
		
		List<BusLineMapPoint> list = getBusLineMapPoint(lineUuid, lineType);
		return getBusLineMapPoint(list, stationSequence);
	}
	/**
	 * 【缓存】获取线路地图线路站点线路，两个站点之间的地图线段(包括两个端点)
	 * @throws BusLineMapPointException 
	 */
	public List<BusLineMapPoint> getBusLineMapPoint(String lineUuid, String lineType, int startStationSequence, int endStationSequence){
		
		List<BusLineMapPoint> rtList = new ArrayList<BusLineMapPoint>();
		List<BusLineMapPoint> list = getBusLineMapPoint(lineUuid, lineType);
		
		BusLineMapPoint startMapPoint = getBusLineMapPoint(list, startStationSequence);
		BusLineMapPoint endMapPoint = getBusLineMapPoint(list, endStationSequence);
		
		if(startMapPoint == null || endMapPoint == null){
//			throw new BusLineMapPointException(PMSUtils.format("找不到lineUuid:{} lineType:{} 站点序号:{} 对于线路地图上的点", lineUuid, lineType, endStationSequence));
		}
		
		try {
			rtList = list.subList(list.indexOf(startMapPoint), list.indexOf(endMapPoint) + 1);
		} catch (Exception e) {
//			e.printStackTrace();
		}
		
		return rtList;
	}
	
	/**
	 * 【缓存】根据站点序号，获取地图线段站点坐标,没有（找不到）返回null
	 * @param list
	 * @param stationSequence
	 * @return
	 */
	private BusLineMapPoint getBusLineMapPoint(List<BusLineMapPoint> list, int stationSequence){
		
		for(BusLineMapPoint mapPoint : list) {
			if(mapPoint.getMlWhichstop() == stationSequence){
				return mapPoint;
			}
		}
		return null;
	}
	
	
	/**
	 * 【缓存】获取线路的地图线段。
	 * 
	 * @param lineUuid
	 * @param lineType 线路类型（上行、下行）
	 * @return
	 */
	public List<BusLineMapPoint> getBusLineMapPoint(String lineUuid, String lineType) {
		
		if(PMSUtils.isEmpty(lineType)){
			try {
				throw new Exception("未指定线路类型");
			} catch (Exception e) {
				e.printStackTrace();
				return new ArrayList<BusLineMapPoint>();
			}
		}

		String cacheKey = lineUuid + "-" + lineType;

		List<BusLineMapPoint> lineMapPoints = EHCacheUtils.getValue(LINE_MAP_LINE, cacheKey);
		if (lineMapPoints != null) {
			return lineMapPoints;
		}

		lineMapPoints = selectListBusLineMapPoint(lineUuid, lineType);
		for (BusLineMapPoint ml : lineMapPoints) {
			ml.setUtmPoint(CoordinateConversion.latLon2UTM(Double.valueOf(ml.getMlLat()), Double.valueOf(ml.getMlLng())));
		}

		EHCacheUtils.setValue(LINE_MAP_LINE, cacheKey, lineMapPoints);

		return lineMapPoints;
	}

	/**
	 * 【数据库】查询线路段坐标信息
	 * @param lineUuid
	 * @param lineType
	 * @return
	 */
	public List<BusLineMapPoint> selectListBusLineMapPoint(String lineUuid, String lineType){
		
		List<BusLineMapPoint> list = busLineMapPointMapper.selectMapLine(lineUuid, lineType);
//		for (MapPoint mapPoint : list) {
//			
//			if(PMSUtils.isEmpty(mapPoint.getMlLat()) || !PMSUtils.isDouble(mapPoint.getMlLat())
//					|| PMSUtils.isEmpty(mapPoint.getMlLng()) || !PMSUtils.isDouble(mapPoint.getMlLng())
//					|| PMSUtils.isEmpty(mapPoint.getMlDistance()) || !PMSUtils.isDouble(PMSUtils.isNull(mapPoint.getMlDistance()))
//					){
//				logger.error("【BusLineService】【数据库】查询线路段坐标信息错误 lineUuid:{} lineType{}", lineUuid, lineType);
//				return new ArrayList<MapPoint>();
//			}
//		}
		
		return list;
	}

	/**
	 * 根据gps坐标获取线路上最近的线路点(获取该点前后位置的点，最多3个点，最少2个点)
	 * @param lineMapPoints
	 * @param latitude
	 * @param longitude
	 * @param stationSeq (站点信息可能导致问题，详见方法说明)
	 * @return
	 */
	public List<BusLineMapPoint> getNearstPoints(List<BusLineMapPoint> lineMapPoints, String latitude, String longitude, Integer stationSeq) {
		// 地图最近点数组游标 从0开始
		int nearstPointIndex = -1;
		if (stationSeq != null) {
			// 注意该方法有缺陷，见方法说明
			nearstPointIndex = getNearstPointIndex(lineMapPoints, latitude, longitude, stationSeq); // 根据经纬度，获取最近的站点信息的 list数组的index
		} else {
			nearstPointIndex = getNearstPointIndex(lineMapPoints, latitude, longitude); // 根据经纬度，获取最近的站点信息的
		}

		List<BusLineMapPoint> nearPoints = new ArrayList<BusLineMapPoint>();

		if (nearstPointIndex < 0) {
			return nearPoints;
		}

		
		return getNearstPoints(lineMapPoints, nearstPointIndex);

	}
	
	/**
	 *  根据gps坐标获取线路上最近的线路点(获取该点前后位置的点，最多3个点，最少2个点)
	 * @param lineUuid
	 * @param lineType
	 * @param latitude
	 * @param longitude
	 * @return
	 */
	public List<BusLineMapPoint> getNearstPoints(String lineUuid, String lineType, String latitude, String longitude){
		return getNearstPoints(lineUuid, lineType, latitude, longitude, null);
	}
	
	/**
	 * 根据gps坐标获取线路上最近的线路点
	 * @param lineUuid
	 * @param lineType
	 * @param latitude
	 * @param longitude
	 * @return
	 */
	public BusLineMapPoint getNearstPoint(String lineUuid, String lineType, String latitude, String longitude){
		List<BusLineMapPoint> points = getBusLineMapPoint(lineUuid, lineType);
		return getNearstPoint(points, latitude, longitude);
	}
	
	/**
	 * 根据gps坐标获取线路上最近的线路点(获取该点前后位置的点，最多3个点，最少2个点)
	 * @param lineUuid
	 * @param lineType
	 * @param latitude
	 * @param longitude
	 * @param stationSeq
	 * @return
	 */
	public List<BusLineMapPoint> getNearstPoints(String lineUuid, String lineType, String latitude, String longitude, Integer stationSeq){
		List<BusLineMapPoint> lineMapPoints = getBusLineMapPoint(lineUuid, lineType);
		// 地图最近点数组游标 从0开始
		int nearstPointIndex = -1;
		if(stationSeq != null){
			// 注意该方法有缺陷，见方法说明
			nearstPointIndex = getNearstPointIndex(lineMapPoints, latitude, longitude, stationSeq);	//  根据经纬度，获取最近的站点信息的 list数组的index
		}else{
			nearstPointIndex = getNearstPointIndex(lineMapPoints, latitude, longitude);	//  根据经纬度，获取最近的站点信息的 list数组的index
		}
		List<BusLineMapPoint> nearPoints = new ArrayList<BusLineMapPoint>();
		
		if(nearstPointIndex < 0){
			return nearPoints;
		}
		return getNearstPoints(lineMapPoints, nearstPointIndex);
	}
	
	/**
	 * 根据最近的站点信息的 list数组的index，获取该点前后位置的点，最多3个点，最少2个点
	 * @param lineMapPoints
	 * @param nearstPointIndex
	 * @return
	 */
	private List<BusLineMapPoint> getNearstPoints(List<BusLineMapPoint> lineMapPoints, int nearstPointIndex) {
		List<BusLineMapPoint> nearPoints = new ArrayList<BusLineMapPoint>();
		try {
			if (lineMapPoints.size() > 0) {
				// 前一个点
				if (nearstPointIndex > 0) {
					nearPoints.add(lineMapPoints.get(nearstPointIndex - 1));
				}

				if (lineMapPoints.size() < nearstPointIndex) {
					logger.debug("找不到最近的站点");
				}
				// 当前点
				nearPoints.add(lineMapPoints.get(nearstPointIndex));

				// 后一个点
				if (nearstPointIndex < lineMapPoints.size() - 1) {
					nearPoints.add(lineMapPoints.get(nearstPointIndex + 1));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage());
		}
		return nearPoints;
	}
	
	/**
	 * 获取GPS点在线路地图上映射最近的点的线路地图数据index值 <br>
	 * 【注意该方法有个缺陷】1、必须确保 数据库存储站点数与报站文件一致；2、如果是原地报站（即不是实际站点gps信息），可能会出错（直线图上来回跳）
	 * 
	 * @param busLineMapPoints
	 *            线路地图点信息
	 * @param lat
	 * @param lng
	 * @param lineStationSeq
	 *            站点序号
	 * @return
	 */
	private int getNearstPointIndex(List<BusLineMapPoint> busLineMapPoints, String lat, String lng, Integer lineStationSeq) {

		/* ************* 根据站序辅助计算 最近地图位置点 start *********************************/
		List<BusLineMapPoint> mapPointList = null;
		if(lineStationSeq != null){
			// 【相对距离辅助方法计算】
			// 【注意该方法有个缺陷】1、必须确保 数据库存储站点数与报站文件一致；2、如果是原地报站（即不是实际站点gps信息），可能会出错（直线图上来回跳）
			mapPointList = getBusLineMapPointByStationSeq(busLineMapPoints, lineStationSeq);
		}else{
			mapPointList = busLineMapPoints;
		}
		/* ************* 根据站序辅助计算 最近地图位置点    end  *********************************/
		
		return getNearstPointIndex(mapPointList, lat, lng); // 实际游标
	}
	
	/**
	 *  获取GPS点在线路地图上映射最近的点的线路地图数据index值
	 * @param points	线路地图点信息
	 * @param lat
	 * @param lng
	 * @return
	 */
	private int getNearstPointIndex(List<BusLineMapPoint> points, String lat, String lng) {
		double dis = Double.MAX_VALUE;
		int index = -1;
		
		for (int i = 0; i < points.size(); i++) {
			BusLineMapPoint ml = points.get(i);
			double d = GpsUtils.getDistance(ml.getMlLat(), ml.getMlLng(), lat, lng);
			if (d < dis) {
				dis = d;
				index = ml.getMlSequence(); // 数据库
			}
		}
		return index -1 ; // 实际游标
	}
	
	private BusLineMapPoint getNearstPoint(List<BusLineMapPoint> points, String lat, String lng) {
		double dis = Double.MAX_VALUE;
		int index = -1;
		BusLineMapPoint rt = null;
		for (int i = 0; i < points.size(); i++) {
			BusLineMapPoint ml = points.get(i);
			double d = GpsUtils.getDistance(ml.getMlLat(), ml.getMlLng(), lat, lng);
			if (d < dis) {
				dis = d;
				index = ml.getMlSequence(); // 数据库
				rt = ml;
			}
		}
		return rt;
	}
	
	/**
	 * 【相对距离辅助方法计算】根据站序，获取地图线上 当前站点 前、后站 间的地图点
	 * @param busLineMapPoints
	 * @param currentStatSeq
	 * @return
	 */
	private List<BusLineMapPoint> getBusLineMapPointByStationSeq(List<BusLineMapPoint> busLineMapPoints, Integer currentStatSeq){
		boolean isLastSeq = false;
		
		int lastSeq = 0;
		for (int j = busLineMapPoints.size()-1; j >=0; j--) {
			BusLineMapPoint mapPoint = busLineMapPoints.get(j);
			if(mapPoint.getMlWhichstop() > 0){
				lastSeq = mapPoint.getMlWhichstop();
				break;
			}
		}
		
		if(lastSeq == currentStatSeq){
			isLastSeq = true;
		}
		
		List<BusLineMapPoint> subMapPoints = null;
		if (currentStatSeq == 1) {
			// 第一站，取第一站和第二站间的所有点
			subMapPoints = busLineMapPoints.subList(0, getMapPointIndexByStationSeq(busLineMapPoints, 2));
		} else if (isLastSeq) {
			// 最后一站、取前一站和当前站的所有点
			int befortStationIndex = getMapPointIndexByStationSeq(busLineMapPoints, currentStatSeq - 1);
			subMapPoints = busLineMapPoints.subList(befortStationIndex, busLineMapPoints.size());
		} else {
			// 中间站，取前一站和后一站间的所有点
			int befortStationIndex = getMapPointIndexByStationSeq(busLineMapPoints, currentStatSeq - 1);
			int afterStationIndex = getMapPointIndexByStationSeq(busLineMapPoints, currentStatSeq + 1);
			subMapPoints = busLineMapPoints.subList(befortStationIndex, afterStationIndex+1);
		}
		
//		for (MapPoint mapPoint : subMapPoints) {
//			if(mapPoint.getMlWhichstop() == currentStatSeq){
//				System.out.println(mapPoint.getMlLineType() + "\t " + mapPoint.getMlSequence() + "\t【" + mapPoint.getMlWhichstop()+ " 】");
//			}else{
//				System.out.println(mapPoint.getMlLineType() + "\t " + mapPoint.getMlSequence() + "\t" + mapPoint.getMlWhichstop());
//				
//			}
//		}
		return subMapPoints;
	}
	
	/**
	 * 【相对距离辅助方法计算】获取站点在当前线路地图点的数组位置
	 * @param allPoints	当前线路所有地图位置
	 * @param stationSeq 站点序号
	 * @return
	 */
	private int getMapPointIndexByStationSeq(List<BusLineMapPoint> allPoints, int stationSeq){
		
		int size = allPoints.size();
		int index = 0;
		for (int i = 0; i < size; i++) {
			BusLineMapPoint mapPoint = allPoints.get(i);
			
			if(mapPoint.getMlWhichstop() == stationSeq){
				
//				System.out.println("找到站点:" + stationSeq);
				index = i;
//				index = mapPoint.getMlSequence();
				break;
			}
		}
		
		return index;
	}

	

	/**
	 * 是否偏线
	 * @param points	线路地图点信息
	 * @param lat
	 * @param lng
	 * @return
	 */
	public boolean isDeviate(List<BusLineMapPoint> points, String lat, String lng) {
		return isDeviate(points, Double.parseDouble(lat), Double.parseDouble(lng));
	}
	
	/**
	 * 是否偏线	
	 * @param points 线路地图点信息
	 * @param lat
	 * @param lng
	 * @return
	 */
	public boolean isDeviate(List<BusLineMapPoint> points, Double lat, Double lng) {

		UTMPoint pbNow = CoordinateConversion.latLon2UTM(lat, lng);
		
		Coordinate[] coordinates = new Coordinate[points.size()];
		for (int i = 0; i < points.size(); i++) {
			BusLineMapPoint ml = points.get(i);
			UTMPoint up = CoordinateConversion.latLon2UTM(Double.valueOf(ml.getMlLat()), Double.valueOf(ml.getMlLng()));
			coordinates[i] = new Coordinate(up.getX(), up.getY());
		}

		Geometry gline = new GeometryFactory().createLineString(coordinates);
		BufferOp pyBufferOp = new BufferOp(gline);
		pyBufferOp.setEndCapStyle(BufferParameters.CAP_ROUND);
		Geometry glineBuffer = pyBufferOp.getResultGeometry(maxOffLine);

		Geometry gNow = new GeometryFactory().createPoint(new Coordinate(pbNow.getX(), pbNow.getY()));

		return !glineBuffer.contains(gNow);
	}
	
	
}
